home *** CD-ROM | disk | FTP | other *** search
/ Info-Mac 4 / Info_Mac IV CD-ROM (Pacific HiTech Inc.)(August 1994).iso / Development / Source / ThreadLib 1.0d4 / README < prev    next >
Text File  |  1994-03-16  |  30KB  |  652 lines

  1. ===========
  2. Description
  3. -----------
  4.  
  5. Thread Library implements nonpreemptive multiple thread execution within
  6. a single application. It does not require any extensions, should work
  7. with all Macintosh models (from the Plus on up), and works with
  8. systems 6.0 (tested on 6.0.5) under Finder or MultiFinder, and
  9. system 7.0. Thread Library compiles into a small library of under 3K,
  10. so it won't add much overhead to your application. A simple test
  11. application and THINK C project demonstrate how threads are used.
  12. Another simple test application compares the speed of Thread Library
  13. with the speed of Apple's Thread Manager. (Thread Library is about
  14. 2 to 3 times faster!) Best of all, the source code, entirely in C,
  15. is free.
  16.  
  17. Every thread has its own stack, and there are no restrictions on the
  18. objects that can be allocated on a thread's stack. All other global
  19. application data are shared by the threads. Context switches are very
  20. efficient since they involve only a few operations to save the current
  21. thread's state, followed by a longjmp to the new thread, and a few
  22. instructions to restore the thread's state.
  23.  
  24. Thread Library was written using THINK C 5.0.4. Some minor changes may
  25. be needed to port it to other compilers. All suggestions and enhancements
  26. are welcome.
  27.  
  28. (c) Copyright 1994 Ari Halberstadt. See the file Distribution for
  29. distribution terms.
  30.  
  31. ============================
  32. What is in this Distribution
  33. ----------------------------
  34.  
  35. Demos
  36.     Applications that demonstrate the use of Thread Library
  37. Demos:ThreadsTest:
  38.     Demonstrates how to use threads in an application
  39. Demos:ThreadsTimed:
  40.     Times threads using Thread Library and Thread Manager
  41. Distribution
  42.     Distribution policy, copyright notice, my address
  43. README
  44.     This document
  45. ThreadLib
  46.     Folder containing the source code for Thread Library
  47. Version History
  48.     Documents the changes made to each version of Thread Library
  49.  
  50. ====================
  51. Using Thread Library
  52. --------------------
  53.  
  54. To add threads to your application, you need to add the file "ThreadLib.c"
  55. to your project. The segment containing Thread Library must be kept in
  56. memory so that the calls to longjmp will work.
  57.  
  58.     WARNING: The segment containing the thread library must not be unloaded
  59.     while there are any threads.
  60.  
  61. The source code is heavily commented, so you should be able to follow how
  62. to use threads and how threads are implemented. This README file contains
  63. comments extracted from the source code that describe the functional interface
  64. to Thread Library. The file "ThreadsTest.c" contains the code for the
  65. ThreadsTest application; you can look at it to see how threads are used in a
  66. simple application. You can also examine the file "ThreadsTimed.c", which
  67. contains the source to the ThreadsTimed application. At a minimum, you should
  68. read this README file before trying to use Thread Library in your application.
  69.  
  70. Before you use Thread Library, you should run the ThreadsTest application
  71. (in the "Demos" folder). The test application displays a dialog with four
  72. lines. The first two lines contain two counters, each incremented in its own
  73. thread. The dialog is updated once a second by a third thread; the third line
  74. shows the number of ticks elapsed between updates, and should be close to
  75. 60 ticks. The fourth line shows the number of ticks remaining until the test
  76. ends. If Thread Manager is installed, then the test is first run using Thread
  77. Manager; this test is used to determine if there is a problem with Thread
  78. Library or with threads in general. If the test of Thread Library doesn't run
  79. correctly, then you should disable all extensions and try running the test
  80. application again. If the application still doesn't run correctly, then you
  81. may have discovered a bug in Thread Library; please contact me (my address is
  82. in the file "Distribution") and I'll try to figure out what's wrong. I would
  83. also like to know if Thread Library is incompatible with any extensions or
  84. control panels.
  85.  
  86. Functional Interface
  87. --------------------
  88.  
  89. *** Stack Sniffer
  90.  
  91. When you define THREAD_STACK_SNIFFER as 1, Thread Library installs a
  92. VBL task that checks for stack overflow every tick. This is similar to the
  93. stack sniffer VBL task installed by the system. It is a good idea to
  94. enable the stack sniffer during debugging. The stack sniffer is enabled
  95. by default if THREAD_STACK_SNIFFER is not already defined and THREAD_DEBUG
  96. is not zero.
  97.  
  98. *** Error Handling
  99.  
  100.     OSErr ThreadError(void)
  101.  
  102. * ThreadError returns the last error that occurred, or noErr if the last
  103. routine completed successfully.
  104.  
  105. *** Thread Serial Numbers
  106.  
  107. Every thread is assigned a unique serial number. Serial numbers are used
  108. to refer to threads, rather than using a pointer, since there is always
  109. the possiblity that a thread may have terminated before a thread pointer
  110. is used, which would make the thread pointer invalid. The specific
  111. assignment of serial numbers to threads is not defined by the interface,
  112. though every valid thread is guaranteed a non-zero serial number. You
  113. should not assume that any thread will have a specific serial number.
  114.  
  115. *** Accessing the Queue of Threads
  116.  
  117.     short ThreadCount(void)
  118.  
  119. * ThreadCount returns the number of threads in the queue.
  120.  
  121.     ThreadType ThreadMain(void)
  122.  
  123. * ThreadMain returns the main thread, or THREAD_NONE if there are no threads.
  124.  
  125.     ThreadType ThreadActive(void)
  126.  
  127. * ThreadActive returns the currently active thread, or THREAD_NONE if
  128. there are no threads.
  129.  
  130.     ThreadType ThreadFirst(void)
  131.  
  132. * ThreadFirst returns the first thread in the queue of threads, or
  133. THREAD_NONE if there are no threads.
  134.  
  135.     ThreadType ThreadNext(ThreadType thread)
  136.  
  137. * ThreadNext returns the next thread in the circular queue of threads.
  138.  
  139. *** Thread Status
  140.  
  141.     ThreadStatusType ThreadStatus(ThreadType thread)
  142.  
  143. * ThreadStatus returns the status of the thread as set with ThreadStatusSet.
  144. A new thread is initially assigned THREAD_STATUS_NORMAL. You can call
  145. ThreadStatus periodically from within each thread (passing the result of
  146. ThreadActive as the thread parameter) and should take whatever action is
  147. specified by the return value. For instance, if ThreadStatus returns
  148. THREAD_STATUS_QUIT, it means that the application is quitting and you
  149. should exit the thread. Depending on the operation the thread is
  150. performing, you may want to display an alert asking the user if
  151. the thread should be exited.
  152.  
  153.     void ThreadStatusSet(ThreadType thread, ThreadStatusType status)
  154.  
  155. * ThreadStatusSet sets the status code for the thread. It is the
  156. responsibility of each thread to call ThreadStatus to determine what
  157. action should be taken. For instance, when the user quits the application,
  158. the application should call ThreadStatusSet with a THREAD_STATUS_QUIT
  159. parameter for each thread in the queue of threads. Then, the application
  160. should call ThreadYield, waiting for all other threads to exit before the
  161. application itself exits. If you prefer not use the thread's status to
  162. indicate to a thread that it should quit, then you could use some global
  163. variable, say gQuitting, which the thread could check periodically.
  164.  
  165. Status values from THREAD_STATUS_NORMAL through THREAD_STATUS_RESERVED are
  166. reserved for use by Thread Library. All other values can be used by the
  167. application for its own purposes.
  168.  
  169. *** Application Defined Data
  170.  
  171.     void *ThreadData(ThreadType thread)
  172.  
  173. * ThreadData returns the data field of the thread. The application can
  174. use the thread's data field for its own purposes.
  175.  
  176.     void ThreadDataSet(ThreadType thread, void *data)
  177.  
  178. * ThreadDataSet sets the data field of the thread. The application can
  179. use the thread's data field for its own purposes.
  180.  
  181. *** Information About the Stack
  182.  
  183.     size_t ThreadStackMinimum(void)
  184.  
  185. * ThreadStackMinimum returns the recommended minimum stack size for
  186. a thread. Thread Library doesn't enforce a lower limit on the
  187. stack size, but it is a good idea to allow at least this many bytes
  188. for a thread's stack.
  189.  
  190.     size_t ThreadStackDefault(void)
  191.  
  192. * ThreadStackDefault returns the default stack size for a thread. This
  193. is the amount of stack space reserved for a thread if a zero stack size
  194. is passed to ThreadBegin.
  195.  
  196.     size_t ThreadStackSpace(ThreadType thread)
  197.  
  198. * ThreadStackSpace returns the amount of stack space remaining in the
  199. specified thread. There are at least the returned number of bytes
  200. between the thread's stack pointer and the bottom of the thread's
  201. stack, though slightly more space may be available to the application
  202. due to overhead from Thread Library.
  203.  
  204.     NOTE: The trap StackSpace will return incorrect results if called from
  205.     any thread other than the main thread. Likewise, using ApplLimit, HeapEnd,
  206.     or CurStackBase to determine the bounds of a thread's stack will produce
  207.     incorrect results when used outside of the main thread. Instead of calling
  208.     StackSpace, use ThreadStackSpace to determine the amount of free stack
  209.     space in a thread.
  210.     
  211. *** Support for Segmentation
  212.  
  213.     void ThreadStackFrame(ThreadType thread, ThreadStackFrameType *frame)
  214.  
  215. * ThreadStackFrame returns information about the specified thread's
  216. stack and stack frame. This information is needed for executing
  217. a stack trace during automatic segment unloading in "SegmentLib.c",
  218. which is part of Winter Shell. You should never need to call this
  219. function. This function will work correctly even if no threads exist.
  220.  
  221. *** Scheduling
  222.  
  223. The three functions ThreadSchedule, ThreadActivate, and ThreadYield
  224. handle the scheduling and context switching of threads. These functions
  225. will be executed the most often of any of the functions in this file, and
  226. therefore will have the greatest impact on the efficiency of Thread
  227. Library. If you find Thread Library's context switches too slow, try
  228. improving the efficiency of these functions.
  229.  
  230.     void ThreadSleepSet(ThreadType thread, ThreadTicksType sleep)
  231.  
  232. * ThreadSleepSet sets the amount of time that the specified thread will
  233. remain inactive.  The 'sleep' parameter specifies the maximum amount
  234. of time that the thread can remain inactive. The larger the sleep value,
  235. the more time is available for execution of other threads. When called
  236. from the main thread, you can pass a sleep parameter equal to the maximum
  237. interval between null events; if no null events are needed, you can pass
  238. a sleep value of THREAD_TICKS_MAX. The main thread will continue to receive
  239. processing time whenever an event is pending and when no other threads are
  240. scheduled (see ThreadSchedule). If the thread is already active, the sleep
  241. time specified will be used when the thread is inactive and is thus eligible
  242. for scheduling by ThreadSchedule. ThreadSleepSet is normally called by
  243. ThreadYield, but you may need to use it if you call ThreadSchedule or
  244. ThreadActivate.
  245.  
  246.     ThreadType ThreadSchedule(void)
  247.  
  248. * ThreadSchedule returns the next thread to activate. Threads are maintained
  249. in a queue and are scheduled in a round-robbin fashion. Starting with the
  250. current thread, the queue of threads is searched for the next thread whose
  251. wake time has arrived. The first such thread found is returned. 
  252.     
  253. In addition to the round-robbin scheduling shared with all threads, the
  254. main thread will also be activated if any events are pending in the event
  255. queue. The application can then immediately handle the events, allowing
  256. the application to remain responsive to user actions such as mouse clicks.
  257. The main thread will also be activated if no other threads are scheduled
  258. for activation, which allows the application either to continue with
  259. its main processing or to call WaitNextEvent and sleep until a thread
  260. needs to be activated or some other task or event needs to be handled.
  261.  
  262. Since ThreadSchedule calls EventAvail (via EventPending), background
  263. applications will continue to receive processing time, even if the main
  264. thread is never activated while some compute intensive thread is executing.
  265. But, since EventAvail can be a slow trap (especially when it yields the
  266. processor to another application), it is only executed every few ticks.
  267.  
  268. Note: if I figure out a faster way to test for events then the call
  269. to EventAvail may be removed, and background applications won't
  270. get time when ThreadSchedule is called. (OSEventAvail won't work
  271. since it doesn't return update or activate events.)
  272.  
  273.     void ThreadActivate(ThreadType thread)
  274.  
  275. * ThreadActivate activates the specified thread. The context switch is
  276. accomplished by saving the CPU context with setjmp and then calling
  277. longjmp, which jumps to the environment saved with setjmp when the thread
  278. being activated was last suspended. We don't have to do any assembly
  279. language glue since setjmp saved the value of the stack pointer, which
  280. at the time of the call to setjmp pointed somewhere in the thread's stack.
  281. The longjmp instruction will restore the value of the stack pointer and
  282. will jump to the statement from which to resume the thread. Longjmp also
  283. handles the saving and restoring of all registers.
  284.  
  285.     void ThreadYield(ThreadTicksType sleep)
  286.  
  287. * ThreadYield activates the next scheduled thread as determined by
  288. ThreadSchedule. The 'sleep' parameter has the same meaning as the
  289. parameter to ThreadSleepSet.
  290.  
  291.     ThreadTicksType ThreadYieldInterval(void)
  292.  
  293. * ThreadYieldInterval returns the maximum time till the next call to
  294. ThreadYield. The interval is computed by subtracting the current time
  295. from each thread's wake time, giving the amount of time that each
  296. thread can remain inactive. The minimum of these times gives the
  297. maximum amount of time till the next call to ThreadYield. The wake
  298. time of the current thread is ignored, since the thread is already
  299. active. You can use the returned value to determine the maximum sleep
  300. value to pass to WaitNextEvent.
  301.  
  302. *** Thread Creation and Destruction
  303.  
  304.     void ThreadEnd(ThreadType thread)
  305.  
  306. * ThreadEnd removes the thread from the queue and disposes of the memory
  307. allocated for the thread. If the thread is the active thread then the
  308. next scheduled thread is activated. All threads (other than the main
  309. thread) must be disposed of before the main thread can be disposed of.
  310.  
  311.     ThreadType ThreadBeginMain(ThreadProcType suspend, ThreadProcType resume,
  312.         void *data)
  313.  
  314. * ThreadBeginMain creates the main application thread and returns the main
  315. thread's serial number. You must call this function before creating any
  316. other threads with ThreadBegin. You must also call MaxApplZone before calling
  317. this function. The 'resume', 'suspend', and 'data' parameters have the
  318. same meanings as the parameters to ThreadBegin.
  319.  
  320. There are several important differences between the main thread and
  321. all subsequently created threads.
  322.  
  323. - The main thread is responsible for handling events sent to the
  324. application, and is therefore scheduled differently than other threads;
  325. see ThreadSchedule for details.
  326.  
  327. - While other threads don't begin executing until they're scheduled to
  328. execute, the main thread is made the active thread and starts to run as
  329. soon as ThreadBeginMain returns.
  330.  
  331. - Since other threads have a special entry point, they are automatically
  332. disposed of when that entry point returns. The main thread, lacking
  333. any special entry point, must be disposed of by the application. You
  334. should call ThreadEnd, passing it the thread returned by ThreadBeginMain,
  335. before exiting your application.
  336.  
  337. - The main thread uses the application's stack and context; no private
  338. stack is allocated for the main thread. Initially, there is therefore
  339. no need to change the context to start executing the thread, and
  340. no special entry point is required. But, like all other threads, the main
  341. thread's context will be saved whenever it is suspended to allow another
  342. thread to execute, and its context will be restored when it is resumed.
  343.  
  344.     ThreadType ThreadBegin(ThreadProcType entry,
  345.         ThreadProcType suspend, ThreadProcType resume,
  346.         void *data, size_t stack_size)
  347.  
  348. * ThreadBegin creates a new thread and returns the thread's serial number.
  349. You must create the main thread with ThreadBeginMain before you can call
  350. ThreadBegin. The 'entry' parameter is a pointer to a function that is
  351. called to start executing the thread. The 'suspend' parameter is a pointer
  352. to a function called whenever the thread is suspended. You can use the
  353. 'suspend' function to save additional application defined context for
  354. the thread. The 'resume' parameter is a pointer to a function called
  355. whenever the thread is resumed. You can use the 'resume' function to
  356. restore additional application defined context for the thread. The
  357. 'data' parameter is passed to the 'entry', 'suspend', and 'resume'
  358. functions and may contain any application defined data.
  359.  
  360. The 'stack_size' parameter specifies the size of the stack needed by
  361. the thread. The requested stack size should be large enough to contain
  362. all function calls, local variables and parameters, and any operating
  363. system routines that may be called while the thread is active (including
  364. interrupt driven routines). If 'stack_size' is zero then the default
  365. stack size returned by ThreadStackDefault is used. It is a good idea to
  366. set the stack size to at least the value returned by ThreadStackMinimum;
  367. otherwise, your application is likely to crash somewhere inside the
  368. operating system. If your thread crashes try increasing the thread's stack
  369. size. You can enable the stack sniffer VBL task (see Stack Sniffer above)
  370. to help detect insufficient stack space. In addition, a stack overflow
  371. will often result in a corrupted heap, since the stack is allocated as
  372. a nonrelocatable block in the heap and overflow usually overwrites the
  373. block's header. For this reason, you can often detect stack overflow by
  374. enabling a "heap check" option in a low-level debugger such as TMON or
  375. MacsBug.
  376.  
  377. The new thread is appended to the end of the thread queue, making it
  378. eligible for scheduling whenever ThreadYield is called. ThreadBegin
  379. returns immediately after creating the new thread. The thread, however,
  380. is not executed immediately, but rather is executed whenever it is
  381. scheduled to execute. At that time, the function specified in the 'entry'
  382. parameter is called. When the function has returned, the thread is removed
  383. from the queue of threads and its stack and any private storage allocated
  384. by ThreadBegin are disposed of.
  385.  
  386. Debugging
  387. ---------
  388.  
  389. When you start using Thread Library, you should disable all optimizations
  390. and should enable all debug code in Thread Library by ensuring that the
  391. preprocessor symbols NDEBUG and THREAD_DEBUG are undefined (THREAD_DEBUG
  392. defaults to true). This will enable a VBL task that will help catch stack
  393. overflow in threads (you can enable the VBL task even when debug code has
  394. been disabled; see Stack Sniffer, above). Thread Library also includes
  395. numerous assertions intended to catch run-time errors. These assertions will
  396. be enabled when the debug code is enabled. Once you know that threads work
  397. with your application, you can enable compiler optimizations and test your
  398. application again to make sure it still runs.
  399.  
  400. If an assertion fails, the DebugStr trap is executed with a simple message.
  401. To help me fix the error, please report the problem to me. Your report should
  402. include a stack crawl generated with your debugger when the assertion failed
  403. (to determine where in the code the assertion failed), the version of Thread
  404. Library you're using, a description of the operating environment (Macintosh
  405. model, system software, extensions, etc.) and a description, if possible, of
  406. the actions preceding the failure.
  407.  
  408. The debug code (mostly assertions) greatly reduces the speed of Thread Library,
  409. and also increases the size of the object code. You will therefore probably
  410. want to disable the debug code when you're confident that your threads are
  411. operating correctly. To disable the debug code, define the preprocessor
  412. symbol NDEBUG or define THREAD_DEBUG as zero.
  413.  
  414.     WARNING: Using the THINK C debugger to trace through context switches
  415.     may result in corruption of the application's heap followed by a nasty
  416.     crash. The problem arrises if you place a breakpoint or try to step
  417.     too close to the longjmp that accomplishes the context switch in the
  418.     function ThreadActivatePtr in the file "ThreadLib.c". I have used TMON
  419.     Professional to successfully trace through the context switches, and
  420.     other low-level debuggers (like MacsBug) should also work.
  421.     
  422. Profiling
  423. ---------
  424.  
  425. You will probably not be able to profile an application that uses threads.
  426. The profiler supplied with THINK C will not work properly when threads
  427. are used, though it could be modified to work correctly with threads.
  428.  
  429. Handling Errors
  430. ---------------
  431.  
  432. I'm not quite sure of the best way to handle errors. In my own applications I
  433. prefer to use exceptions to handle errors, but, since there is no standard
  434. exception handling mechanism, this is not an appropriate error handling
  435. method for a reusable library intended to be used in many applications written
  436. by different people. A more standardized solution is for functions that
  437. can fail to return an error code. This is a somewhat inelegant solution,
  438. as it requires functions to return a mostly useless error code instead
  439. of a meaningful result; an extra temporary variable must often be
  440. created and passed by reference to the function to hold the function's
  441. result. A problem also arrises if a function that formerly did not
  442. return an error is modified in a newer version to return an error code.
  443. Unless all functions were originally written to return error codes,
  444. there is now no way for the application to detect the error.
  445.  
  446. I prefer that functions return some meaningful value (not some useless
  447. error code), and that procedures return nothing. One solution that
  448. accommodates this preference is to have a special function that returns
  449. the error code generated by the last function called. This is the
  450. approach taken with managers like the Memory Manager and parts of
  451. the Resource Manager. For now, this seems like the best solution
  452. for detecting errors, and it is the one used in the current version
  453. of Thread Library. I do not guarantee that this method of detecting
  454. errors will not change in the future.
  455.  
  456. Handling Errors with Exceptions
  457. -------------------------------
  458.  
  459. If your application uses exceptions to handle errors, then you'll need to
  460. add a custom context switching routine to your threads. Most implementations
  461. of exceptions work by modifying the program counter and stack pointer to
  462. jump to an exception handling routine. The code needed to raise an exception
  463. typically keeps track of which exception handler to jump to in some global
  464. variables. A problem can occur if the exception implementation attempts to
  465. jump to a routine and stack address for an inactive thread. For instance,
  466. in the following code:
  467.  
  468.     void thread1(void *data) {
  469.     {
  470.         TRY { /* this sets up the exception handling environment */
  471.             (void) ThreadBegin(thread2, NULL, NULL, NULL, 0);
  472.             while (! done)
  473.                 ThreadYield(0);
  474.         } CATCH { /* this is executed on failure */
  475.             cleanup();
  476.         } ENDTRY; /* this closes up the exception handler */
  477.     }
  478.     
  479.     void thread2(void *data)
  480.     {
  481.         while (! done_allocating_memory()) {
  482.             if (! allocate_some_memory())
  483.                 FailOSErr(memFullErr); /* this raises an exception */
  484.             ThreadYield(0);
  485.         }
  486.     }
  487.  
  488. both thread1 and thread2 have their own private stack and CPU state.
  489. When thread2 raises an exception, the exception raising code will
  490. attempt to jump to the last exception handler specified. But
  491. the last exception handler was specified when thread1 was
  492. executing. Since the exception raising code does not know
  493. about other threads or about Thread Library, it can not
  494. properly switch contexts when the exception is raised. This
  495. situation will probably result in a mysterious crash. Since
  496. the problem will only occur under extraordinary circumstances
  497. (e.g., running out of memory), it will also be hard to reproduce
  498. and debug.
  499.  
  500. When you create a thread  you need to allocate memory to save the state
  501. of the exception handler, and you need to install your own custom context
  502. switching routines. The suspend function must save a copy of the state of
  503. the exception handler, while the resume function must restore the state of
  504. the exception handler. Thread Library already includes code to handle the
  505. exchange of the exception state for Winter Shell (you'll need Winter
  506. Shell 1.0d3 or later to use threads; the latest released version is
  507. currently only 1.0d2).
  508.  
  509. You must also be careful to prevent threads from propagating beyond a
  510. thread's entry point. For instance, a thread's entry point could be
  511. written as follows:
  512.  
  513.     void thread(void *data)
  514.     {
  515.         TRY {
  516.             ... do stuff ...
  517.         } CATCH {
  518.             ... cleanup ...
  519.             NOPROPAGATE; /* this prevents exceptions from propagating */
  520.         } ENDTRY;
  521.     }
  522.  
  523. when an exception is raised in the thread, it will be prevented from
  524. propagating beyond the thread's entry point by the NOPROPAGATE statement.
  525.  
  526.     Winter Shell Note: The exception handlers in Winter Shell 1.0d3 will
  527.     not propagate outside of the entry point, so the NOPROPAGATE statement
  528.     is not strictly necessary.
  529.  
  530. ==============
  531. Known Problems
  532. --------------
  533.  
  534. Toolbox
  535. -------
  536.  
  537. For all threads other than the main thread, some Maintosh Toolbox
  538. routines may not work correctly if the stack is not between the region of
  539. memory defined by the low-memory globals CurStackBase and ApplLimit.
  540. Possibly prohibited are some QuickDraw calls, but I don't actually know
  541. which Toolbox routines will fail. Some simple tests I ran created a dialog
  542. with a progress bar; created, opened, read and wrote files; created a
  543. resource file and added resources to it; allocated memory; and did various
  544. other operations, all successfully and without problems.  Since the main
  545. thread uses the application's stack, there are no restrictions on
  546. the Toolbox routines that the main thread may call. I am interested
  547. in whether you encounter (or don't encounter) limitations to Toolbox calls,
  548. and would like to know under what conditions the limitations arise.
  549.  
  550. SuperClock
  551. ----------
  552.  
  553. SuperClock 4.0.4 won't update its numerals every second when EventAvail is
  554. called from within a thread.  SuperClock will still update the timer
  555. animation when you run the stopwatch, and will update the numerals less
  556. frequently. This happens both in my Thread Library and in Apple's Thread
  557. Manager. You usually won't notice this effect with Apple's Thread Manager
  558. since it doesn't call EventAvail (it looks directly at the EventQueue
  559. low-memory global) and since threads rarely need to call EventAvail. My
  560. Thread Library needs to call EventAvail, however, since I don't know how
  561. to detect all possible events by looking at low-memory globals. I'm not
  562. sure if this should be considered a bug in threads or in SuperClock. I'll
  563. have to contact the author of SuperClock to find out what he thinks.
  564. At any rate, this appears to be a pretty minor cosmetic problem; it should
  565. not interfere with the operation of an application that uses Thread
  566. Library. (Thanks to Daniel Sears <sears@netcom.com> for reporting this.)
  567.  
  568. =====
  569. To Do
  570. -----
  571.  
  572. It shouldn't be too difficult to add preemptive threads to Thread Library.
  573. Preemptive threads have a limited utility, however, since they must be
  574. executed at interrupt time, precluding the use of most of the Macintosh
  575. toolbox. Someday, if there's demand for preemptive threads, I may add
  576. this feature.
  577.  
  578. Some low-memory globals may not be available under A/UX (most notably, the
  579. Ticks low-memory global). Adding a runtime check for A/UX to determine if
  580. the Ticks low-memory global is available could marginally slow down access
  581. to the variable, so it may be better to include a conditional compilation
  582. option.
  583.  
  584. =======
  585. Credits
  586. -------
  587.  
  588. Some ideas on how to use setjmp/longjmp to swap stacks were adapted
  589. from the source for Task Manager v2.2.1 by Michael Hecht
  590. <Michael_Hecht@mac.sas.com>, available at the info-mac archives
  591. and various other sites.
  592.  
  593. Special thanks to Peter Lewis <peter.lewis@info.curtin.edu.au>, who
  594. did a detailed review of Thread Library and made numerous suggestions
  595. to successive versions, including using serial numbers to refer to
  596. all threads, using a sentinel value in the stack-sniffer VBL task,
  597. and improving the scheduling of threads.
  598.  
  599. Thanks also to all of the following people for helping me make Thread
  600. Library a better product.
  601.  
  602. Anton Rang <rang@icicle.winternet.mpls.mn.us> responded to my query on
  603. Comp.sys.mac.programmer on how to disable the stack sniffer VBL task.
  604. (Several other people also responded, but Anton Rang's reply was the
  605. first to arrive.)
  606.  
  607. Daniel Sears <sears@netcom.com> reported some problems with Thread Library
  608. and tried out updated versions I emailed to him.
  609.  
  610. Matthew Xavier Mora <mxmora@unix.sri.com> suggested the SetPort call
  611. in TestThreads and helped debug the "update" problem in ThreadsTest.
  612.  
  613. Barry Kirsch <bkirsch@NADC.NADC.NAVY.MIL> reported a problem with compiling
  614. Thread Library using THINK C's "MacHeaders" precompiled header.
  615.  
  616. ==========================
  617. Why I Wrote Thread Library
  618. --------------------------
  619.  
  620. After the first release on the internet, there was some discussion on
  621. the news group Comp.sys.mac.programmer as to why anyone would bother writing
  622. or using an implementation of threads when Apple has already provided the
  623. Thread Manager. The short answer is that: I wanted to see how hard it would be
  624. to implement threads; Thread Library is compatible with system 6.0;
  625. Thread Library doesn't require the user to install any extensions; Thread
  626. Library is significantly faster than Thread Manager; and Apple charges
  627. $200 to license Thread Manager while the source code for Thread Library
  628. is free, which is especially important to authors of freeware and shareware
  629. applications.
  630.  
  631. ========
  632. Epilogue
  633. --------
  634.  
  635. If it took me, a single programmer, 6 days to get threads up and
  636. running (and that not even full-time), why did it take Apple many
  637. years and a big fancy extension to get around to implementing
  638. threads? This hack isn't very difficult. I had something sort-of
  639. working the first day, but it took me six days to get it reasonably
  640. stable and to put in all the verbose comments. It would have been
  641. even easier to implement had I had access to proprietary Apple
  642. information. If you look at the Thread Manager extension, it's really
  643. very simple, and it doesn't do much more than what this library does.
  644. (I developed this library prior to examining what the Thread Manager
  645. does and did not try to copy the Thread Manager.)
  646.  
  647. Since the initial release (v1.0d1), I've spent some more time tweaking
  648. Thread Library and have received assistance from people on the internet.
  649. Some features were added, other features were removed, the size of the
  650. object code was reduced, it was made faster, and more documentation was
  651. added.
  652.